﻿/** \file
* \$Rev: 187 $
* 
* \$Date: 2010-04-27 21:58:51 +0000 (Tue, 27 Apr 2010) $
*
* \$URL: http://chorus-twitter.googlecode.com/svn/trunk/src/utils/KeyedCollection.cs $
*/

using System;
using System.Collections.Generic;
using chorus.twitter;

namespace chorus.utils
{
	/// <summary>
	/// A class that implements the IList interface, and internally uses an
	/// IdDictionary. The list stores items derived from Keyed. I don't like
	/// that I have to do the type-unsafe IList to get control binding to
	/// work.
	/// </summary>
	/// <typeparam name="ValueType">The element type (user, status, etc.)</typeparam>
	public class KeyedCollection<ValueType> :
		IList<ValueType>,
		System.Collections.IList
		where ValueType : Mergeable, IKeyed<ulong>
	{
		/// <summary>The dictionary, indexed by numeric ID.</summary>
		public readonly KeyedDictionary<ulong, ValueType> byid =
			new KeyedDictionary<ulong, ValueType>();

		#region IList members

		/// <summary>Whether the list is synchronized.</summary>
		bool System.Collections.ICollection.IsSynchronized { get { return false; } }
		/// <summary>Whether the list is of a fixed size.</summary>
		bool System.Collections.IList.IsFixedSize { get { return false; } }
		/// <summary>This seems complicated... read the documentation.</summary>
		object System.Collections.ICollection.SyncRoot { get { return this; } }
		/// <summary>Copy this list to an array. Not implemented due to bad type safety issues.</summary>
		/// <param name="array">The array to copy to.</param>
		/// <param name="index">The index at which copying should start.</param>
		void System.Collections.ICollection.CopyTo(Array array, int index) { throw new NotImplementedException(); }
		/// <summary>Remove an object from the list.</summary>
		/// <param name="value">The object to remove.</param>
		void System.Collections.IList.Remove(object value) { Remove((ValueType)value); }
		/// <summary>Insert an object into the list.</summary>
		/// <param name="index">The index at which the object should be inserted.</param>
		/// <param name="value">The object to insert.</param>
		void System.Collections.IList.Insert(int index, object value) { Insert(index, (ValueType)value); }
		/// <summary>Get the index of a value in the list.</summary>
		/// <param name="value">The value to search for.</param>
		/// <returns>The index of the value, or -1 if the value is not in the list.</returns>
		int System.Collections.IList.IndexOf(object value) { return IndexOf((ValueType)value); }
		/// <summary>Check to see if the list contains a value.</summary>
		/// <param name="value">The value to search for.</param>
		/// <returns>Whether that value is in the list.</returns>
		bool System.Collections.IList.Contains(object value) { return Contains((ValueType)value); }
		/// <summary>The indexing operator [].</summary>
		/// <param name="index">The index to look up.</param>
		/// <returns>The object at that index.</returns>
		object System.Collections.IList.this[int index]
		{
			get { return this[index]; }
			set { this[index] = (ValueType)value; }
		}
		/// <summary>Add an object to the list.</summary>
		/// <param name="value">The object to add.</param>
		/// <returns>The index at which the object has been added.</returns>
		int System.Collections.IList.Add(object value) { return IndexOf(AddMerge((ValueType)value)); }
		
		#endregion

		#region IList<ValueType> members

		/// <summary>The number of items in the list.</summary>
		public int Count { get { return byid.Count; } }
		/// <summary>Whether the list is read-only.</summary>
		public bool IsReadOnly { get { return false; } }
		/// <summary>Get a type-unsafe enumerator.</summary>
		/// <returns>The enumerator.</returns>
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return byid.Values.GetEnumerator(); }
		/// <summary>Get a type-safe enumerator.</summary>
		/// <returns>The enumerator.</returns>
		public IEnumerator<ValueType> GetEnumerator() { return byid.Values.GetEnumerator(); }
		/// <summary>Copy this list to an array.</summary>
		/// <param name="array">The array to copy to.</param>
		/// <param name="index">The index at which copying should start.</param>
		public void CopyTo(ValueType[] array, int index) { byid.Values.CopyTo(array, index); }
		/// <summary>Add an object to the list.</summary>
		/// <param name="value">The object to add.</param>
		public void Add(ValueType value) { AddMerge(value); }
		/// <summary>Remove an object from the list.</summary>
		/// <param name="value">The object to remove.</param>
		/// <returns>True if the item existed in the list and was removed.</returns>
		public virtual bool Remove(ValueType value) { return byid.Remove(value); }
		/// <summary>Remove an object at the specified index.</summary>
		/// <param name="index">The index of the object to be removed.</param>
		public void RemoveAt(int index) { Remove(this[index]); }
		/// <summary>Clear the contents of the list.</summary>
		public virtual void Clear() { byid.Clear(); }
		/// <summary>Insert a value at the specified index.</summary>
		/// <param name="index">The index at which the object should be inserted. THIS IS IGNORED.</param>
		/// <param name="value">The value to insert.</param>
		public void Insert(int index, ValueType value) { AddMerge(value); }
		/// <summary>The indexing operator [].</summary>
		/// <param name="index">The index to look up.</param>
		/// <returns>The value at that index.</returns>
		public ValueType this[int index]
		{
			get { return byid.IndexToVEnum(index).Current; }
			set { byid[byid.IndexToKEnum(index).Current] = value; }
		}
		/// <summary>Check whether the list contains a value.</summary>
		/// <param name="value">The value to look up.</param>
		/// <returns>Whether that item is in the list.</returns>
		public bool Contains(ValueType value)
		{
			if (value == null)
				return false;
			return byid.ContainsKey(value.Key);
		}
		/// <summary>Search for a value in the list.</summary>
		/// <param name="value">The value to search for.</param>
		/// <returns>The index of that value in the list.</returns>
		public int IndexOf(ValueType value)
		{
			IEnumerator<ValueType> en = byid.Values.GetEnumerator();
			for (int i = 0; ; i++)
			{
				if (!en.MoveNext())
					return -1;
				if (en.Current == value)
					return i;
			}
		}

		#endregion

		/// <summary>
		/// Add a value to the collection, extracting the key from the value
		/// automatically. If the value already exists in the collection,
		/// attempt to merge it instead of replacing it.
		/// </summary>
		/// <param name="value">The value to add.</param>
		/// <returns>The resulting value in the collection.</returns>
		public virtual ValueType AddMerge(ValueType value) { return byid.AddMerge(value); }

		/// <summary>Add a range of values to the collection.</summary>
		/// <param name="vals">The values to add.</param>
		public void AddRange(IEnumerable<ValueType> vals)
		{
			foreach (ValueType val in vals)
				Add(val);
		}
	}
	
	/// <summary>
	/// A collection of items derived from twitter.user.
	/// </summary>
	/// <typeparam name="ValueType">The element type (user or account).</typeparam>
	public class UserCollection<ValueType> : KeyedCollection<ValueType>
		where ValueType : user
	{
		/// <summary>
		/// The dictionary of users indexed by screen_name.
		/// </summary>
		public readonly KeyedDictionary<string, ValueType> byname =
			new KeyedDictionary<string, ValueType>();

		/// <summary>
		/// Add a value to the collection, extracting the key from the value
		/// automatically. If the value already exists in the collection,
		/// attempt to merge it instead of replacing it.
		/// </summary>
		/// <param name="value">The value to add.</param>
		/// <returns>The resulting value in the collection.</returns>
		public override ValueType AddMerge(ValueType value)
		{
			if (base.Contains(value))
				return byname.AddMerge(base.AddMerge(value));
			return base.AddMerge(byname.AddMerge(value));
		}

		/// <summary>Remove an object from the list.</summary>
		/// <param name="value">The object to remove.</param>
		/// <returns>True if the item existed in the list and was removed.</returns>
		public override bool Remove(ValueType value)
		{
			// Need to use a non-shortcircuiting 'or'
			return base.Remove(value) | byname.Remove(value);
		}

		/// <summary>Clear the contents of the list.</summary>
		public override void Clear()
		{
			base.Clear();
			byname.Clear();
		}
	}
}
